﻿namespace DomainServices.Tools
{
    using System;
    using System.CodeDom;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Linq;
    using System.ServiceModel.DomainServices;
    using System.ServiceModel.DomainServices.Server;

    internal class InvokeOperationProxyGenerator : ProxyGenerator
    {
        private DomainServiceDescription _domainServiceDescription;
        private CodeTypeDeclaration _proxyClass;

        public InvokeOperationProxyGenerator(CodeDomClientCodeGenerator clientProxyGenerator, CodeTypeDeclaration proxyClass, DomainServiceDescription domainServiceDescription) : base(clientProxyGenerator)
        {
            this._proxyClass = proxyClass;
            this._domainServiceDescription = domainServiceDescription;
        }

        public override void Generate()
        {
            foreach (DomainOperationEntry entry in from m in this._domainServiceDescription.DomainOperationEntries
                where m.Operation == DomainOperation.Invoke
                orderby m.Name
                select m)
            {
                this.GenerateInvokeOperation(entry, true);
                this.GenerateInvokeOperation(entry, false);
            }
        }

        private void GenerateInvokeOperation(DomainOperationEntry domainOperationEntry, bool generateCallback)
        {
            CodeMemberMethod method;
            string methodName = domainOperationEntry.Name;
            if (generateCallback && Enumerable.Any<CodeTypeMember>(this._proxyClass.Members.Cast<CodeTypeMember>(), c => (c.Name == methodName) && (c.GetType() != typeof(CodeMemberMethod))))
            {
                base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_NamingCollision_MemberAlreadyExists, new object[] { this._proxyClass.Name, methodName }));
            }
            else
            {
                CodeTypeReference reference = null;
                Type type = CodeGenUtilities.TranslateType(domainOperationEntry.ReturnType);
                CodeTypeReference reference2 = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.InvokeOperation", (string) this._proxyClass.UserData["Namespace"], false);
                if (type != typeof(void))
                {
                    Type nonNullableType = System.ServiceModel.DomainServices.TypeUtility.GetNonNullableType(type);
                    if (nonNullableType.IsEnum)
                    {
                        string errorMessage = null;
                        if (!base.ClientProxyGenerator.CanExposeEnumType(nonNullableType, out errorMessage))
                        {
                            base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Domain_Op_Enum_Error, new object[] { methodName, this._proxyClass.Name, nonNullableType.FullName, errorMessage }));
                            return;
                        }
                        base.ClientProxyGenerator.RegisterUseOfEnumType(nonNullableType);
                    }
                    reference = CodeGenUtilities.GetTypeReference(type, base.ClientProxyGenerator, this._proxyClass);
                    reference.Options |= CodeTypeReferenceOptions.GenericTypeParameter;
                    reference2.TypeArguments.Add(reference);
                }
                CodeMemberMethod method2 = new CodeMemberMethod {
                    Attributes = MemberAttributes.Public | MemberAttributes.Final,
                    Name = methodName,
                    ReturnType = reference2
                };
                method = method2;
                this._proxyClass.Members.Add(method);
                ReadOnlyCollection<DomainOperationParameter> parameters = domainOperationEntry.Parameters;
                string comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_Invoke_Method_Summary_Comment, new object[] { domainOperationEntry.Name });
                method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
                foreach (DomainOperationParameter parameter in parameters)
                {
                    comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_Invoke_Method_Parameter_Comment, new object[] { parameter.Name });
                    method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(parameter.Name, comment, base.ClientProxyGenerator.IsCSharp));
                }
                if (generateCallback)
                {
                    method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", DomainServices.Tools.Resource.CodeGen_DomainContext_Invoke_Method_Callback_Parameter_Comment, base.ClientProxyGenerator.IsCSharp));
                    method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("userState", DomainServices.Tools.Resource.CodeGen_DomainContext_Invoke_Method_UserState_Parameter_Comment, base.ClientProxyGenerator.IsCSharp));
                }
                method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(DomainServices.Tools.Resource.CodeGen_DomainContext_Invoke_Returns_Comment, base.ClientProxyGenerator.IsCSharp));
                IEnumerable<Attribute> attributes = domainOperationEntry.Attributes.Cast<Attribute>();
                CustomAttributeGenerator.GenerateCustomAttributes(base.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_ThrewException_CodeMethod, new object[] { ex.Message, method.Name, this._proxyClass.Name, ex.InnerException.Message }), attributes, method.CustomAttributes, method.Comments);
                List<CodeExpression> list = new List<CodeExpression> {
                    new CodePrimitiveExpression(methodName),
                    new CodeTypeOfExpression(reference)
                };
                CodeVariableReferenceExpression targetObject = new CodeVariableReferenceExpression("parameters");
                if (parameters.Count > 0)
                {
                    CodeTypeReference reference3 = CodeGenUtilities.GetTypeReference(typeof(Dictionary<string, object>), base.ClientProxyGenerator, this._proxyClass);
                    CodeVariableDeclarationStatement statement = new CodeVariableDeclarationStatement(reference3, "parameters", new CodeObjectCreateExpression(reference3, new CodeExpression[0]));
                    method.Statements.Add(statement);
                }
                foreach (DomainOperationParameter parameter2 in parameters)
                {
                    Type enumType = System.ServiceModel.DomainServices.TypeUtility.GetNonNullableType(parameter2.ParameterType);
                    if (enumType.IsEnum)
                    {
                        string str3 = null;
                        if (!base.ClientProxyGenerator.CanExposeEnumType(enumType, out str3))
                        {
                            base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Domain_Op_Enum_Error, new object[] { method.Name, this._proxyClass.Name, enumType.FullName, str3 }));
                            return;
                        }
                        base.ClientProxyGenerator.RegisterUseOfEnumType(enumType);
                    }
                    CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(parameter2.ParameterType), base.ClientProxyGenerator, this._proxyClass), parameter2.Name);
                    IEnumerable<Attribute> enumerable2 = parameter2.Attributes.Cast<Attribute>();
                    string customCommentHeader = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_Parameter_FailedToGenerate, new object[] { parameter2.Name });
                    CustomAttributeGenerator.GenerateCustomAttributes(base.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_ThrewException_CodeMethodParameter, new object[] { ex.Message, paramDecl.Name, method.Name, this._proxyClass.Name, ex.InnerException.Message }), enumerable2, paramDecl.CustomAttributes, method.Comments, customCommentHeader);
                    method.Parameters.Add(paramDecl);
                    method.Statements.Add(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(targetObject, "Add"), new CodeExpression[] { new CodePrimitiveExpression(parameter2.Name), new CodeVariableReferenceExpression(parameter2.Name) }));
                }
                if (parameters.Count > 0)
                {
                    list.Add(targetObject);
                }
                else
                {
                    list.Add(new CodePrimitiveExpression(null));
                }
                InvokeAttribute operationAttribute = (InvokeAttribute) domainOperationEntry.OperationAttribute;
                list.Add(new CodePrimitiveExpression(operationAttribute.HasSideEffects));
                if (generateCallback)
                {
                    CodeTypeReference reference4 = CodeGenUtilities.GetTypeReference(typeof(System.Action).FullName, (string) this._proxyClass.UserData["Namespace"], false);
                    reference4.TypeArguments.Add(reference2);
                    method.Parameters.Add(new CodeParameterDeclarationExpression(reference4, "callback"));
                    list.Add(new CodeVariableReferenceExpression("callback"));
                    method.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(object), base.ClientProxyGenerator, this._proxyClass), "userState"));
                    list.Add(new CodeVariableReferenceExpression("userState"));
                }
                else
                {
                    list.Add(new CodePrimitiveExpression(null));
                    list.Add(new CodePrimitiveExpression(null));
                }
                CodeExpression expression2 = new CodePrimitiveExpression(null);
                if (parameters.Count > 0)
                {
                    expression2 = targetObject;
                }
                CodeExpressionStatement statement2 = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "ValidateMethod", new CodeExpression[] { new CodePrimitiveExpression(methodName), expression2 }));
                method.Statements.Add(statement2);
                CodeMethodReferenceExpression expression3 = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "InvokeOperation");
                CodeExpression expression = new CodeMethodInvokeExpression(expression3, list.ToArray());
                if (type != typeof(void))
                {
                    expression = new CodeCastExpression(reference2, expression);
                }
                method.Statements.Add(new CodeMethodReturnStatement(expression));
            }
        }
    }
}

